#include "NodeMsg.h"
#include "ia.h"
#include "Base64.h"
#include <windows.h>
#include <vector>
#include <iostream> 
#include <sstream> 
#include <string> 

#define NAME		"NodeMsg"
#define UUID		"CB1B363ED30A5E6B8F6050FAEC4B43F2"

#define IMAGE3D_HEAD_LENGTH    (4+4+8)

CBaseSample* CNodeMsg::CreateInstance()
{
	return new CNodeMsg();
}

CNodeMsg::CNodeMsg()
{
	_strName = NAME;
	_strUuid = UUID;
	_nSubType = 0;

	MsgType = ENodeMsgTypes::IA_TAG_NODE_MSG_TYPE_REQUEST;
	MsgDataType = ENodeMsgDataTypes::IA_TAG_NODE_MSG_DATA_TYPE_TEXT;
	StatusCode = 0;

	BinDataLen = 0;
	pBinData = NULL;
}

CNodeMsg::~CNodeMsg()
{
	BinDataLen = 0;
	IA_FREE(pBinData);
}

CNodeMsg* CNodeMsg::Copy()
{
	CNodeMsg* pMsg = new CNodeMsg();

	pMsg->MsgType = MsgType;
	pMsg->MsgDataType = MsgDataType;
	pMsg->StatusCode = StatusCode;
	pMsg->SrcAddr = SrcAddr;
	pMsg->SubSrcAddr = SubSrcAddr;
	pMsg->DstAddr = DstAddr;
	pMsg->SubDstAddr = SubDstAddr;
	pMsg->Command = Command;
	pMsg->Message = Message;
	if (BinDataLen != 0) {
		IA_MALLOC(pMsg->pBinData, unsigned char*, 0, BinDataLen);
		pMsg->BinDataLen = BinDataLen;
		memcpy(pMsg->pBinData, pBinData, BinDataLen);
	}

	return pMsg;
}

int CNodeMsg::SetBinDataByString(std::string str)
{
	if (str.empty()) IA_RETURN_OK

	int x = str.length() + 1;
	IA_MALLOC(pBinData, unsigned char*, BinDataLen, x);

	BinDataLen = x;
	memcpy(pBinData, str.c_str(), str.length());
	// In case the original size is bigger than string length
	// force end the string here to avoid dirty data
	pBinData[str.length()] = '\0';

	IA_RETURN_OK
}

std::string CNodeMsg::ToString()
{
	std::string s = "Command: " + Command;
	s += ", MsgType: " + std::to_string(MsgType);
	s += ",  MsgDataType: " + std::to_string(MsgDataType);
	s += ",  StatusCode: " + std::to_string(StatusCode);
	s += ",  SrcAddr: " + SrcAddr;
	s += ",  SubSrcAddr: " + SubSrcAddr;
	s += ",  DstAddr: " + DstAddr;
	s += ",  SubDstAddr: " + SubDstAddr;
	s += ",  Message: " + Message;
	s += ",  BinDataLen: " + std::to_string(BinDataLen);
	if (pBinData != NULL) {
		if (MsgDataType == ENodeMsgDataTypes::IA_TAG_NODE_MSG_DATA_TYPE_IMAGE3D) {
			s += "image 3d data not shown";
		}
		else {
			s += ",  pBinData(as string): " + std::string((char*)pBinData);
		}
	}

	return s;
}


std::string CNodeMsg::Flatten()
{
	std::string flatMsg;
	
	flatMsg.append(std::to_string(MsgType));
	flatMsg += '\n';
	flatMsg.append(std::to_string(MsgDataType));
	flatMsg += '\n';
	flatMsg.append(std::to_string(StatusCode));
	flatMsg += '\n';
	flatMsg.append(SrcAddr);
	flatMsg += '\n';
	flatMsg.append(Base64::Encode((unsigned char*)SubSrcAddr.c_str(), SubSrcAddr.length()));
	flatMsg += '\n';
	flatMsg.append(DstAddr);
	flatMsg += '\n';
	flatMsg.append(Base64::Encode((unsigned char*)SubDstAddr.c_str(), SubDstAddr.length()));
	flatMsg += '\n';
	flatMsg.append(Command);
	flatMsg += '\n';
	flatMsg.append(Base64::Encode((unsigned char*)(&Message[0]), Message.length()));
	flatMsg += '\n';
	flatMsg.append(std::to_string(BinDataLen));
	flatMsg += '\n';
	if (BinDataLen > 0) {
		flatMsg.append(Base64::Encode(pBinData, BinDataLen));
	}
	else {
		flatMsg.append("0");
	}

	return flatMsg;
}

int CNodeMsg::Expand(std::string flattenStr)
{
	const int itemsCnt = 11;

	std::vector<std::string> mapItems;
	std::string::size_type pos1, pos2;
	pos2 = flattenStr.find('\n');
	pos1 = 0;
	while (std::string::npos != pos2) {
		mapItems.push_back(flattenStr.substr(pos1, pos2 - pos1));
		pos1 = pos2 + 1;
		pos2 = flattenStr.find('\n', pos1);
	}
	mapItems.push_back(flattenStr.substr(pos1));

	IA_ASSERT(mapItems.size() == itemsCnt);
	try {
	MsgType = (ENodeMsgTypes)std::stoi(mapItems[0]);
	MsgDataType = (ENodeMsgDataTypes)std::stoi(mapItems[1]);
	StatusCode = (ENodeMsgTypes)std::stoi(mapItems[2]);
	SrcAddr = mapItems[3];
	SubSrcAddr = Base64::Decode(mapItems[4]);
	DstAddr = mapItems[5];
	SubDstAddr = Base64::Decode(mapItems[6]);
	Command = mapItems[7];
	Message = Base64::Decode(mapItems[8]);
	int oldbinLen = BinDataLen;
	BinDataLen = std::stoi(mapItems[9]);
	if (BinDataLen != 0) {
		IA_MALLOC(pBinData, unsigned char*, oldbinLen, BinDataLen);
		Base64::Decode(mapItems[10], pBinData, BinDataLen);
	}
	}
	catch (...) {
		IA_ERROR("Failed to expand msg: %s", flattenStr.c_str());
		IA_RETURN_ERR
	}
	IA_RETURN_OK
}

/**
 * @brief Data lsyout
 *		4bytes x_num | 4bytes y_num | 8 bytes reserved | x | y | z
 *
 * @note intensity data is not considered
 */
int CNodeMsg::FillWithImage3D(CImage3D* pimg3D)
{
	IA_ASSERT(pimg3D->GetW() > 0 && pimg3D->GetH() > 0);
	IA_ASSERT(pimg3D->GetPX() && pimg3D->GetPY() && pimg3D->GetPZ());

	MsgDataType = ENodeMsgDataTypes::IA_TAG_NODE_MSG_DATA_TYPE_IMAGE3D;

	int x_num = pimg3D->GetW();
	int y_num = pimg3D->GetH();

	int oldLen = BinDataLen;
	BinDataLen = IMAGE3D_HEAD_LENGTH + /*x*/ x_num * sizeof(float) + /*y*/ y_num * sizeof(float) + /*z*/x_num * y_num * sizeof(float);
	IA_MALLOC(pBinData, unsigned char*, oldLen, BinDataLen);

	pBinData[0] = x_num & 0xFF;
	pBinData[1] = (x_num>>8) & 0xFF;
	pBinData[2] = (x_num >> 16) & 0xFF;
	pBinData[3] = (x_num >> 24) & 0xFF;
	pBinData[4] = y_num & 0xFF;
	pBinData[5] = (y_num >> 8) & 0xFF;
	pBinData[6] = (y_num >> 16) & 0xFF;
	pBinData[7] = (y_num >> 24) & 0xFF;

	memcpy(pBinData + IMAGE3D_HEAD_LENGTH, pimg3D->GetPX(), x_num * sizeof(float));
	memcpy(pBinData + IMAGE3D_HEAD_LENGTH + x_num*sizeof(float), pimg3D->GetPY(), y_num * sizeof(float));
	memcpy(pBinData + IMAGE3D_HEAD_LENGTH + x_num * sizeof(float) + y_num*sizeof(float), pimg3D->GetPZ(), (x_num * y_num) * sizeof(float));

	IA_RETURN_OK
}

int CNodeMsg::ExtraceImage3D(CImage3D* pImage3D)
{
	if (MsgDataType != ENodeMsgDataTypes::IA_TAG_NODE_MSG_DATA_TYPE_IMAGE3D) {
		IA_ERROR("Not considered as a 3D image: %d", MsgDataType);
		IA_RETURN_ERR
	}

	IA_ASSERT(BinDataLen > IMAGE3D_HEAD_LENGTH);
	int x_num = pBinData[0] | ((pBinData[1] << 8)) | ((pBinData[2] << 16)) | ((pBinData[3] << 24));
	int y_num = pBinData[4] | ((pBinData[5] << 8)) | ((pBinData[6] << 16) ) | ((pBinData[7] << 24));
	IA_ASSERT(x_num > 0 && y_num > 0);
	IA_ASSERT(BinDataLen == IMAGE3D_HEAD_LENGTH + x_num * sizeof(float) + y_num * sizeof(float) + x_num * y_num * sizeof(float));

	pImage3D->Create(x_num, y_num);
	memcpy(pImage3D->GetPX(), pBinData + IMAGE3D_HEAD_LENGTH, x_num * sizeof(float));
	memcpy(pImage3D->GetPY(), pBinData + IMAGE3D_HEAD_LENGTH + x_num * sizeof(float), y_num * sizeof(float));
	memcpy(pImage3D->GetPZ(), pBinData + IMAGE3D_HEAD_LENGTH + x_num * sizeof(float) + y_num * sizeof(float), x_num * y_num * sizeof(float));

	IA_RETURN_OK
}
